package org.example.Polls;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;

import android.util.Log;

/**
 * Singleton class responsible for sending SOAP requests based
 * on file-based templates.
 * @author smitten
 */
public class SOAPHelper {
	private static final String TAG = "SOAPHelper";
	
	private static final String TRUST_STORE_RESOURCE = "server.jks";
	private static final String TRUST_STORE_PASSWORD = "uVoteItUP";
	
	// for singleton 
	private static SOAPHelper instance = null;
	
	// cache of templates
	private Map<String, String> templateMap = new HashMap<String,String>();
	
	private HttpURLConnectionHelper urlConnectionHelper;
	
	public SOAPHelper() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
		urlConnectionHelper = new HttpURLConnectionHelper(TRUST_STORE_RESOURCE, TRUST_STORE_PASSWORD);
	}
	
	/**
	 * Helper function to read a file from the classpath into a String 
	 * @param fileName The filename as it exists in the classpath.
	 * @return The contents of the file as a String.
	 * @throws IOException
	 */
	private static String getStringFromFile(String fileName) throws IOException {
		InputStream is = SOAPUserService.class.getResourceAsStream(fileName);
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		StringBuffer sb = new StringBuffer();
		String line;
		while((line = br.readLine())!=null) {
			sb.append(line + "\n");
		}
		return sb.toString();
	}
	
	/**
	 * Gets the corresponding template contents, given the template name.
	 * @param templateName  The name of the template.  This is really just the
	 * prefix up until the last dot in the template filename.
	 * @return
	 */
	private String getTemplate (String templateName) throws IOException {
		if (!templateMap.containsKey(templateName)) {
			String filename = templateName + ".soap";
			String template = getStringFromFile(filename);
			templateMap.put(templateName, template);
		}
		return templateMap.get(templateName);
	}
	
	/**
	 * Gets the one and only instance.
	 * @return
	 * @throws IOException 
	 * @throws CertificateException 
	 * @throws KeyStoreException 
	 * @throws NoSuchAlgorithmException 
	 * @throws KeyManagementException 
	 */
	public static synchronized SOAPHelper getInstance() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
		if (instance == null) {
			instance = new SOAPHelper();
		}
		return instance;
	}
	
	/**
	 * Sends a request to the specified URL as a SOAP message.
	 * @param serviceUrl The URL to send the message to.
	 * @param templateName The name of the SOAP template to use.
	 * @param values The array of values to substitute in the template.
	 * @return A string corresponding to the response.
	 * @throws IOException  If an error occurs connecting to the URL, 
	 * retrieving the template, or reading/writing to the URL.
	 */
	public String sendRequest (URL serviceUrl, String templateName, Object[] values) throws IOException {
		HttpURLConnection urlCon = urlConnectionHelper.openConnection(serviceUrl);
		
		// Set the correct Content-type header, otherwise the axis webserver 
		// will return an  HTTP 500, which results in FileNotFoundException
		urlCon.addRequestProperty("Content-type", "text/xml");
		urlCon.setDoOutput(true);
		
		// Perform the substitution of the template with the given values.
		String template = getTemplate(templateName);
		String request = MessageFormat.format(template, values);
		
		// Write the request to the OutputStream
		Log.d(TAG, "Request = "+request);
		OutputStreamWriter out = new OutputStreamWriter(urlCon.getOutputStream());
		out.write(request);
		out.close();
		
		Log.d(TAG, "ResponseCode = "+urlCon.getResponseCode());
		// Set the input stream based on the response code.
		InputStream is = (urlCon.getResponseCode() == 200) ? urlCon.getInputStream() 
		                                                   : urlCon.getErrorStream();
		
		// Now read the response into a String; the response should be in UTF-8
		// XXX: Are there any cases where the response would not be in UTF-8?
		BufferedReader in = new BufferedReader(new InputStreamReader(is,"UTF-8"));
		StringBuffer response = new StringBuffer();
		String line;
		while((line = in.readLine())!=null) {
			response.append(line+"\n");
		}
		Log.d(TAG, "Response = "+response);
		
		// Return the response as a string.
		return response.toString();
	}
	
}
